#!/usr/bin/python
# -*- coding: utf-8 -*-

import maya.cmds as mc
import maya.mel as mel
import re
from maya import OpenMaya as om
import maya.OpenMayaAnim as oma
import OF3D_PublicClass.searchByName as sbn
import mayaPrint
from strings import Strings
class MeshShape( sbn.Matching ,mayaPrint.MayaPrint ,Strings ):
    def __checkListCyle(self,listA,listB):
        """
        the method deem [255, 256, 244, 243] same as [243, 255, 256, 244]
        """
        #[255, 256, 244, 243]
        #[243, 255, 256, 244]
        if listA==listB:
            return True
            
        if len(listA) != len(listB):
            #print "__checkListCyle len error"
            return False
            
        for i in range( len(listA ) ):
            if listA[i] not in listB:
                #print "__checkListCyle comp not in error"
                return False
        #Blist start index
        idx = listB.index( listA[0] )
        
        for i in range( len(listA ) ):
            if listA[i] != listB[idx]:
                #print listA[i],listB[idx],idx
                return False
            idx+=1
            if idx>len(listA)-1:
                idx = 0
                
        return True
        
    def meshCompare(self,obja,objb,**flags):
        "Compare two plogon topology"
        flagDefine = {"matchNumFace":(True,bool)}
        flagDirect = self.funtionFlag(flagDefine, **flags)
        
        matchNumFace = flagDirect["matchNumFace"]
        
        dagPathA = self.getDagByName(obja)
        dagPathB = self.getDagByName(objb)
        amsh = om.MFnMesh(dagPathA)
        bmsh = om.MFnMesh(dagPathB)
        
        if matchNumFace and amsh.numFaceVertices() != bmsh.numFaceVertices():
            self.mayaError("Topology: vertex %s != %s."%(obja,objb))
            return False
        
        aitmp = om.MItMeshPolygon(dagPathA)
        bitmp = om.MItMeshPolygon(dagPathB)
        
        aVerArry = om.MIntArray()
        bVerArry = om.MIntArray()
        
        while not aitmp.isDone():
            aitmp.getVertices( aVerArry )
            bitmp.getVertices( bVerArry )
            alist = list( aVerArry )
            blist = list( bVerArry )
            if not self.__checkListCyle(alist,blist):
                self.mayaError("Topology:%s != %s."%(obja,objb))
                return False
            aitmp.next()
            bitmp.next()
            #maybe B mesh no next face
            if matchNumFace==False and bitmp.isDone():
                break
        return True
    def mirrorMesh(self,baseGeo,meshA,meshB,realtime=True):
        """OF3D_PublicClass\mesh.py\mirrorMesh
        mirror mesh on other mesh 
        baseGeo : a original mesh shape symmetry no deform
        meshA : get shape from this mesh
        meshB : make the mesh deformed
        realtime : True False
        """
        mc.delete(meshB,ch=True)
        mandrake = self.pluginName( "mandrake" )
        try:
            if mc.pluginInfo( mandrake, query=True,loaded=True )==False:
                mc.loadPlugin( mandrake )
        except:
            pass
        isLoaded = mc.pluginInfo( mandrake, query=True,loaded=True )
        #use mandrake deformr mirror
        if isLoaded:
            self.mayaPrint( "mirror mesh by mandrake" )
            mc.select(meshB)
            mdke = mc.deformer(type="mandrake")
            mc.connectAttr( "%s.outMesh"%(baseGeo),"%s.neutralMesh"%(mdke[0]) )
            mc.connectAttr( "%s.outMesh"%(meshA),"%s.sculptMesh"%(mdke[0]) )
            if realtime==False:
                mc.delete(meshB,ch=True)
        #mirror by wrap
        elif realtime==True:
            self.mirrorMesh(baseGeo,meshA,meshB,False)
            self.mayaPrint( "mirror mesh by wrap" )
            for obj in [meshA,meshB]:
                #unlock attributes
                for atr in ["tx","ty","tz","rx","ry","rz","sx","sy","sz"]:
                    mc.setAttr( "%s.%s"%(obj,atr) ,lock=False)
                #make has same position
                mc.xform(obj,t=[0,0,0],ro=[0,0,0])
            #wrap
            mc.setAttr("%s.scaleX"%(meshB),-1)
            mc.select(meshB,meshA)
            mayaVersion = mc.about(f=1)
            if mayaVersion >= "2011":
                    mel.eval('doWrapArgList "7" { "1","0","1", "2", "0", "1", "0", "0"}')
            else:
                    mel.eval('doWrapArgList "6" { "1","0","1", "2", "0", "1", "0"}')
        #mirror by abSymMesh
        elif realtime==False:
            try:
                #use script abSymMesh.mel
                mel.eval("source abSymMesh")
            except:
                raise TypeError( "Can not find plugin %s or abSymMesh.mel;"%(mandrake) )
            mel.eval("abSymMesh()")
            mc.select(baseGeo)
            mel.eval( 'abSymCtl("sbgBn")' )
            #mc.textField("sbgFld",e=True,text=baseGeo)
            mc.radioButtonGrp("saRbGrp",e=True,select=1)
            mc.checkBox("maChkBx",e=True,value=0)
            mc.checkBox("upoChkBx",e=True,value=1)
            copyOne = mc.duplicate(meshA)
            mc.select(copyOne)
            mel.eval( 'abSymCtl("fsBn")' )
            mc.deleteUI("abSymWin",window=True)
            mc.connectAttr( "%s.outMesh"%(copyOne[0]),"%s.inMesh"%(meshB) )
            mc.blendShape(copyOne[0],meshB,w=[0,1],origin="local")
            mc.delete(meshB,ch=True)
            mc.delete(copyOne[0])
        mc.select(meshB)
        return meshB
        
    def softEdgeShapeOrig(self,*objects,**flags):
        
        flagDefine = {"printWarning":(True,bool),
                      "printResult":(True,bool)}
                      
        flagDirect = self.funtionFlag(flagDefine, **flags)
        pW = flagDirect["printWarning"]
        pR = flagDirect["printResult"]
        # no input get objects form u selected
        if len(objects)==0:
            objects = mc.ls(sl=True)
        if len(objects)==0:
            self.mayaError( "No object selected or nothing input." )
            return
        for obj in objects:
            shapeOrig = self.getMeshOrig( obj )
            if shapeOrig==None:
                try:
                    mc.polySoftEdge( obj, a=180, ch=0)
                    if pW:
                        self.mayaWarning( "<No ShapeOrig> polySoftEdge -a 180 -ch 0 %s;"%( obj ) )
                except Exception,e:
                    print e
            else:
                mc.polySoftEdge( shapeOrig, a=180, ch=1)
                value = mc.getAttr('%s.intermediateObject'%(shapeOrig))
                mc.setAttr( '%s.intermediateObject'%(shapeOrig), 0)
                mc.select(shapeOrig)
                mc.delete(shapeOrig, ch=True)
                mc.setAttr( '%s.intermediateObject'%(shapeOrig), value)
                if pR:
                    self.mayaPrint( "polySoftEdge -a 180 -ch 0 %s;"%( shapeOrig ), addResult=False)
        mc.select(cl=True)
        
    def getMeshOrig(self,inObj):
            #if obj has deform node
            allList = mc.ls(mc.listRelatives(inObj,shapes=True,c=True),type="mesh")
            itsShapes = mc.listRelatives(inObj,shapes=1,pa=1)
            for sp in itsShapes:
                    if sp not in allList:
                            allList.append(sp)
            for obj in allList:
                    value = mc.getAttr('%s.intermediateObject'%(obj))
                    upTrans = mc.listRelatives(obj,parent=True,pa=True)
                    hasOutPut = mc.listConnections("%s.worldMesh"%(obj),d=1)
                    hasInPut = mc.listConnections( "%s.inMesh"%(obj),s=1,d=0 )
                    #self.mayaPrint(upTrans)
                    if value and upTrans==[inObj] and hasOutPut!=None and hasInPut==None:
                            #self.mayaPrint(obj)
                            return obj
    #delete pointer object unuse shapeOrig-----------------------------
    def deleteUnuseOrig(self,inObj,printer=True):
            #if obj has deform node
            allList = mc.ls(mc.listRelatives(inObj,shapes=True,c=True),type="mesh")
            #not mesh shape \return
            if allList==[]:
                    return None
            itsShapes = mc.listRelatives(inObj,shapes=1,pa=1)
            #no shape \return
            if itsShapes==None:
                return None
            
            for sp in itsShapes:
                    if sp not in allList:
                            allList.append(sp)
            for obj in allList:
                    value = mc.getAttr('%s.intermediateObject'%(obj))
                    upTrans = mc.listRelatives(obj,parent=True,pa=True)
                    hasOutPut = mc.listConnections("%s.worldMesh"%(obj),d=1)
                    #self.mayaPrint(upTrans)
                    if value and upTrans==[inObj] and hasOutPut==None:
                            mc.delete(obj)
                            if printer==True:
                                    self.mayaPrint("Delete unused shapeOrig %s."%(obj))
                                    
    def shareFirstOrigToOtherFun(self,inObjs):#inObjs must a list
            if type(inObjs)!=list:
                    self.mayaError("ShapeOrigOprate.getObjsOrig must a list input.")
                    return
            origList = []
            #add meshShape orig to the list
            for o in inObjs:
                    origName = None
                    #get mesh orininal mesh if ! None put it in list else continue
                    if self.getMeshOrig(o) !=None:
                            origName = self.getMeshOrig(o)
                    else:
                            continue
                    #check two mesh topology 1==same topology
                    if self.meshCompare(inObjs[0],origName)!=True:
                            continue
                    elif origName!=None and self.meshCompare(inObjs[0],origName)==True and origName not in origList:
                            origList.append(origName)
            #replace first orig other meshShapeOrig plugin
            if len(origList)<2:
                    return
            mc.select(origList)
            for i in range(1,len(origList)):
                    objAttr = mc.listConnections("%s.worldMesh"%(origList[i]),d=1,p=1)
                    for na in objAttr:#object name and attribute name
                            mc.connectAttr("%s.worldMesh"%(origList[0]),na,f=True)
                            self.mayaPrint("ConnectAttr %s.worldMesh-->%s"%(origList[0],na))
                    mc.delete(origList[i])

    def replaceOrig(self):
            sels=mc.ls(sl=1,ap=1)
            if len(sels)!=2:#how many objs selected
                    self.mayaError("No 2 objects select.")
                    return
            if self.meshCompare(sels[0],sels[1])!=True:#check two object weather has equal topology
                    result = self.confirmDialog("Force Replace ShapeOrig?",["Yes","No"])
                    if result!="Yes":
                            return
            shapename = mc.listRelatives(sels[0],shapes=1,pa=1)[0]
            orig2 = self.getMeshOrig(sels[1])
            objAttr = mc.listConnections("%s.worldMesh"%(orig2),d=1,p=1)
            for na in objAttr:#object name and attribute name
                    mc.connectAttr("%s.worldMesh"%(shapename),na,f=True)
                    self.mayaPrint("%s.worldMesh-->%s"%(shapename,na))
            mc.parent(shapename,sels[1],r=True,s=True)
            mc.delete(orig2,sels[0])
            mc.rename(shapename,self.getLastName(orig2))
            mc.setAttr("%s.intermediateObject"%(orig2),1)
            self.mayaPrint("replace Orig.")
            
    def replaceUV(self,delRefObj=True,printer=True):
            sels=mc.ls(sl=1,ap=1)
            isRefs = mc.referenceQuery(sels[0],isNodeReferenced=1)
            if len(sels)!=2:#how many objs selected
                    self.mayaError("No 2 objects select.")
                    return False
            if self.meshCompare(sels[0],sels[1])!=True:#check two object weather has equal topology
                    self.mayaWarning("Topology %s != %s."%(sels[0],sels[1]))
                    return False
            #funtion main
            #make objShape as current
            tempObjForRepShape = mc.createNode('mesh')
            tempObjForRep = mc.listRelatives(tempObjForRepShape,p=True)
            mc.connectAttr("%s.outMesh"%(sels[0]),"%s.inMesh"%(tempObjForRepShape))
            lisHis = mc.listHistory(sels[1],il=1,pdo=1)
            if lisHis!=None:#has shapeOrig
                    atrList = []
                    #set all of its deformer envelope zero
                    for x in lisHis:
                            if mc.objExists("%s.envelope"%(x)):
                                    oriValue = mc.getAttr("%s.envelope"%(x))
                                    atrList.append(oriValue)
                                    mc.setAttr("%s.envelope"%(x),lock=False)
                                    mc.setAttr("%s.envelope"%(x),0)
                    mc.blendShape(sels[1],tempObjForRep,w=[0,1])
                    mc.delete(tempObjForRep,ch=True)
                    #set all of its deformer envelope original value
                    i=0
                    for x in lisHis:
                            if mc.objExists("%s.envelope"%(x)):
                                    mc.setAttr("%s.envelope"%(x),atrList[i])
                                    i+=1
                    #
                    self.deleteUnuseOrig(sels[1])
                    shapeOrig = self.getMeshOrig(sels[1])
                    if shapeOrig!=None:
                            objAttr = mc.listConnections("%s.worldMesh"%(shapeOrig),d=1,p=1)
                            for na in objAttr:#object name and attribute name
                                    mc.connectAttr("%s.worldMesh"%(tempObjForRepShape),na,f=True)
                                    #self.mayaPrint("%s.worldMesh-->%s"%(tempObjForRepShape,na))
                            mc.parent(tempObjForRepShape,sels[1],r=True,s=True)
                            mc.delete(shapeOrig,tempObjForRep)
                            mc.rename(tempObjForRepShape,self.getLastName(shapeOrig))
                            mc.setAttr("%s.intermediateObject"%(shapeOrig),1)
            else:#there isn't shapeOrig
                    mc.blendShape(sels[1],tempObjForRep,w=[0,1])
                    mc.delete(tempObjForRep,ch=True)
                    targetShapes = mc.listRelatives(sels[1],shapes=True,c=True)[0]
                    mc.connectAttr("%s.outMesh"%(tempObjForRepShape),"%s.inMesh"%(targetShapes))
                    #if straight delete tempObjForRep uv maby wrong so must create a deformer
                    mc.cluster(sels[1])
                    mc.delete(sels[1],ch=True)
                    mc.delete(tempObjForRep)
            mc.select(sels[1])
            if delRefObj==True and isRefs==False:
                    mc.delete(sels[0])
            if printer==True:
                    self.mayaPrint("Replace UV from %s to %s."%(sels[0],sels[1]))
            return True
            
    def delRepeatFace(self,shapeName):
        "delete polygon repeat faces"
        oPath = self.getDagByName(shapeName)
        oMesh = om.MFnMesh(oPath)
        itMeshFV = om.MItMeshPolygon(oPath)
        while not itMeshFV.isDone():
              theFV = om.MIntArray()
              itMeshFV.getVertices(theFV)
              theId = itMeshFV.index()
              itMeshFVCyle = om.MItMeshPolygon(oPath)
              while not itMeshFVCyle.isDone():
                  otheFV = om.MIntArray()
                  itMeshFVCyle.getVertices( otheFV )
                  otheId = itMeshFVCyle.index()
                  if theFV==otheFV and theId!=otheId:
                      mc.delete( "%s.f[%s]"%( shapeName ,otheId) )
                  itMeshFVCyle.next()
              itMeshFV.next()
              
    def adsorbToNearestFace(self):
        """adsorb one object's vertex To other object nearest face.
        """
        selected = mc.ls(sl=True,fl=True)
        grp = mc.createNode("transform")
        objName = selected[-1]
        for x in selected[:-1]:
            wsT = mc.xform(x,q=True,ws=True,t=True)
            mc.xform(grp,ws=True,t=wsT)
            mc.geometryConstraint(objName,grp)
            wsT = mc.xform(grp,q=True,ws=True,t=True)
            mc.xform(x,ws=True,t=wsT)
        mc.delete(grp)
        
    def vertexMoveTo(self):
        "make select vertex move to end select obj."
        sls = mc.ls(sl=True,fl=True)
        objName = sls[-1]
        for v in sls[:-1]:
            bufferList = v.split(".")
            t = mc.xform("%s.%s"%(objName,bufferList[1]),q=1,ws=1,t=1)
            mc.xform(v,ws=1,t=t)
    def isInstance(self,shape):
        #a not exists object name input
        if not mc.objExists(shape):
            self.mayaWarning( "Can not find object %s //"%shape )
            return
        mObject = self.getMObjByName(shape)
        spDadNodeFn = om.MFnDagNode(mObject)
        return spDadNodeFn.isInstanced()
    def unLockMeshNormals(self,ptType=True):
        "set all meshs Unlock Normals and soften Edge //"
        allMeshs = mc.ls(type="mesh")
        for mesh in allMeshs:
            mc.polyNormalPerVertex( mesh, ufn=True )
            polyNormalNodes = mc.ls( mc.listHistory(mesh), type="polyNormalPerVertex" )
            for nod in polyNormalNodes:
                mc.delete(nod)
        for mesh in allMeshs:
            if mc.objExists( mesh ):
                imdObj = mc.getAttr( "%s.intermediateObject"%(mesh) )
                if imdObj==True:
                    continue
                itsParent = mc.listRelatives(mesh,p=1,f=1)
                mc.select(itsParent)
                self.softEdgeShapeOrig(printResult=False)
            else:
                #may be delete mesh at delete -ch time
                print "Check it %s"%( mesh )
        self.mayaPrint("set all meshs Unlock Normals and soften Edge //",ptType)
        return True
#        numParents = spDadNodeFn.parentCount()
#        if numParents==1:
#            return False
#        else:
#            return True
        
MeshShapeCls = MeshShape()